import Gerber from './gerber';
class View3D {
  constructor() {
    this.renderer = null;
    this.has3D = true;
    this.scene = null;
    this.camera = null;
    this.boardControls = null;

    this.repaint = 0;
    this.outline;
    this.checked = true;

    this.gerber = new Gerber();
    this.outlineLayers,
      this.limits,
      this.w,
      this.h,
      this.boardMaterial,
      this.board,
      this.lengths,
      this.boardSides;
    this.layersArr = [];
    this.arr = [];
  }

  /**
   * 移除3D展示
   */
  remove() {
    console.log('remove', this.scene, this.board, this.arr);
    if (!this.board) {
      return;
    }
    this.scene.remove(this.board);
    this.repaint = 0;
    this.boardControls.update();
    this.renderer.render(this.scene, this.camera);
    console.log('remove', this.scene, this.board, this.arr);
  }

  /**
   * 切换2D/3D
   * @param {*} type
   */
  setSwitch(type) {
    this.board.position.x = 0;
    this.board.position.z = 0;
    this.board.position.y = -100;
    this.board.rotation.x = 0;
    this.board.rotation.y = 0;
    this.board.rotation.z = 0;
    if (type == '2D') {
      this.boardControls.noRotate = true;
      this.boardControls.update();
      this.repaint = 0;
    } else {
      this.boardControls.noRotate = false;
    }
  }

  /**
   * 设置偏移量
   * @param {*} offsetX
   * @param {*} offsetZ
   */
  setOffset(offsetX = 0, offsetZ = 0) {
    this.board.position.x = offsetX;
    this.board.position.z = offsetZ;
  }

  /**
   * 选中SMT层显示隐藏
   * @param {*} index
   * @param {*} enabled
   */
  checkSmtUpdate(index, enabled) {
    for (let i = 0; i < this.layersArr.length; i++) {
      if (i == index) {
        this.layersArr[i].enabled = enabled;
      }
    }
    this.repaint = 0;
  }

  /**
   * 获取尺寸
   * @returns
   */
  getDimensions() {
    return { w: this.w, h: this.h };
  }

  /**
   * 显示轮廓
   */
  showOutline() {
    if (this.outline) {
      this.outline.enabled = this.outline.sides.visible = this.checked;
      this.boardSides.visible = !this.checked;
      this.repaint = this.lengths + 1;
      // if (!this.checked) this.updateArea(w, h)
      // this.updateArea(this.outline.maxX - this.outline.minX, this.outline.maxY - this.outline.minY)
      return;
    }
    if (!this.checked) return;

    this.outline = this.gerber.findOutline(this.outlineLayers);
    if (!this.outline.path.length)
      return (this.outline = undefined), alert("Can't find any outline!");
    this.outline.enabled = true;
    this.boardSides.visible = false;
    // this.updateArea(this.outline.maxX - this.outline.minX, this.outline.maxY - this.outline.minY)
    var outlineShape = new THREE.Shape();
    outlineShape.moveTo(
      this.outline.path[0][0] - this.limits.minX - this.w / 2,
      this.h / 2 - (this.outline.path[0][1] - this.limits.minY),
    );
    for (var i = 0; i < this.outline.path.length; i++) {
      var cmd = this.outline.path[i];
      if (cmd.length > 5) {
        var ox = cmd[5],
          oy = cmd[6],
          cx = cmd[0] + ox,
          cy = cmd[1] + oy;
        outlineShape.arc(
          ox,
          -oy,
          Math.sqrt(ox * ox + oy * oy),
          Math.atan2(oy, -ox),
          Math.atan2(-(cmd[3] - cy), cmd[2] - cx),
          !cmd[7],
        );
        outlineShape.moveTo(
          cmd[2] - this.limits.minX - this.w / 2,
          -(cmd[3] - this.limits.minY - this.h / 2),
        );
      } else
        outlineShape.lineTo(
          cmd[2] - this.limits.minX - this.w / 2,
          -(cmd[3] - this.limits.minY - this.h / 2),
        );
    }
    this.outline.sides = outlineShape.extrude({
      amount: 1.54,
      bevelEnabled: false,
      extrudeMaterial: 0,
      material: 1,
    });
    this.outline.sides.materials = [
      this.boardMaterial,
      new THREE.MeshBasicMaterial({ visible: false }),
    ];
    // this.outline.sides.computeVertexNormals()
    this.board.add(
      (this.outline.sides = new THREE.Mesh(this.outline.sides, new THREE.MeshFaceMaterial())),
    );
    this.outline.sides.position.y = 1.54 / 2;
    this.outline.sides.rotation.x = Math.PI / 2;
    this.repaint = this.lengths + 1;
  }

  /**
   * 更新面积
   * @param {*} dw
   * @param {*} dh
   * @returns
   */
  updateArea(dw, dh) {
    var areaMM2 = dw * dh,
      areaIN2 = areaMM2 / 25.4 / 25.4;
    areaMM2 = Math.round(areaMM2 * 100) / 100;
    areaIN2 = Math.round(areaIN2 * 100) / 100;
    return { areaMM2, areaIN2 };
  }

  /**
   * 初始化场景
   */
  initScene() {
    this.scene = new THREE.Scene();
    try {
      this.renderer = new THREE.WebGLRenderer({ antialias: true });
      //this.gerber.ppmm = 20;
      this.renderer.sortObjects = false;
    } catch (e) {
      // debug('Got WebGL error, falling back to 2D canvas.')
      this.has3D = false;
      this.gerber.ppmm = 20;
      this.renderer = new THREE.CanvasRenderer({ antialias: true });
    }

    // this.scene = new THREE.Scene();
    this.camera = new THREE.PerspectiveCamera(50);
    this.camera.up.set(0, 0, 1);
    this.camera.position.set(0, 0, 0);
    this.scene.add(this.camera);

    // Ambient light.
    var ambientLight = new THREE.AmbientLight(0xcccccc);
    this.scene.add(ambientLight);

    // Sun light.
    if (this.has3D) {
      var sunLight = new THREE.SpotLight(0xcccccc, 0.3);
      sunLight.position.set(0, 150000, 0);
      this.scene.add(sunLight);
    }
  }

  /**
   * 初始化3D展示
   * @param {*} layers
   * @param {*} dom
   */
  init(layers, dom) {
    console.log(layers, 'layers');
    this.layersArr = layers;
    this.limits = { minX: Infinity, minY: Infinity, maxX: -Infinity, maxY: -Infinity };
    this.lengths = layers.length;
    this.outlineLayers = layers.filter((x) => x.type == this.gerber.OUTLINE);
    // debugger;
    if (this.outlineLayers.length) layers = layers.filter((x) => x.type != this.gerber.OUTLINE);
    else this.outlineLayers = layers;
    for (var i = 0; i < layers.length; i++) {
      this.gerber.touchLimits(layers[i], this.limits);
      // layers[i].enabled = false;
    }
    (this.w = this.limits.maxX - this.limits.minX), (this.h = this.limits.maxY - this.limits.minY);

    // Board.
    var Material = this.has3D ? THREE.MeshPhongMaterial : THREE.MeshBasicMaterial;
    var bottom = this.gerber.makeBoard(this.w, this.h),
      top = this.gerber.makeBoard(this.w, this.h, true);
    var bottomTexture = new THREE.Texture(bottom),
      topTexture = new THREE.Texture(top);
    this.gerber.clearBoard(bottom), this.gerber.clearBoard(top);
    (bottomTexture.needsUpdate = true), (topTexture.needsUpdate = true);
    var materials = [
      null,
      null,
      new Material({ shininess: 80, ambient: 0xaaaaaa, specular: 0xcccccc, map: topTexture }),
      new Material({ shininess: 80, ambient: 0xaaaaaa, specular: 0xcccccc, map: bottomTexture }),
      null,
      null,
    ];
    if (!this.has3D) (materials[2].overdraw = true), (materials[3].overdraw = true);

    this.board = new THREE.Mesh(
      new THREE.CubeGeometry(
        this.w,
        1.54,
        this.h,
        this.has3D ? 1 : Math.ceil(this.w / 3),
        1,
        this.has3D ? 1 : Math.ceil(this.h / 3),
        materials,
        { px: 0, nx: 0, pz: 0, nz: 0 },
      ),
      new THREE.MeshFaceMaterial(),
    );
    this.board.position.y = -100;
    if (this.has3D) this.scene.add(this.board);

    // Add the sides.
    this.boardMaterial = new Material({
      shininess: 80,
      ambient: 0x333333,
      specular: 0xcccccc,
      color: 0x255005,
    });
    this.boardSides = new THREE.CubeGeometry(this.w, 1.54, this.h, 1, 1, 1, undefined, {
      py: 0,
      ny: 0,
    });
    // this.boardSides.computeVertexNormals();
    this.boardSides = new THREE.Mesh(this.boardSides, this.boardMaterial);
    this.board.add(this.boardSides);
    this.arr.push(this.board);

    console.log(this.arr, 'this.arr');
    console.log(this.board, 'this.board');

    // Create all the holes.
    var holeMaterial = this.boardMaterial.clone();
    holeMaterial.side = THREE.BackSide;
    for (var i = 0; i < layers.length; i++)
      if (!layers[i].type)
        for (var j = 0; j < layers[i].cmds.length; j++) {
          var cmd = layers[i].cmds[j];
          if (cmd[0] != ((1 << 2) | 3)) continue;
          var r = (layers[i].scale * layers[i].shapes[cmd[1]][1]) / 2;
          var hole = new THREE.CylinderGeometry(r, r, 1.54, 32, 0, true);
          //hole.computeVertexNormals();
          hole = new THREE.Mesh(hole, holeMaterial);
          hole.position.x = cmd[2] * layers[i].scale - this.limits.minX - this.w / 2;
          hole.position.z = this.h / 2 - (cmd[3] * layers[i].scale - this.limits.minY);
          this.board.add(hole);
        }

    if (!this.has3D) this.scene.add(this.board);

    this.camera.lookAt(this.board.position);

    this.boardControls = new THREE.ObjectControls(this.board, this.renderer.domElement);

    this.boardControls.camera = this.camera;
    this.boardControls.eye = this.camera.position.clone().subSelf(this.board.position);

    // 检查元素是否存在
    if (dom) {
      // 绑定滚轮事件处理程序
      dom.value.addEventListener('mousewheel', (event) => {
        var delta = event.wheelDelta / 120 || -event.detail / 3;
        this.board.position.y *= 1 - delta * 0.06;
      });
    } else {
      console.error('Element not found.');
    }

    const handleResize = () => {
      this.renderer.setSize(dom.value.clientWidth, dom.value.clientHeight);
      this.camera.aspect = dom.value.clientWidth / dom.value.clientHeight;
      this.camera.updateProjectionMatrix();
      this.boardControls.screen.width = window.innerWidth;
      this.boardControls.screen.height = window.innerHeight;
      this.boardControls.radius = (window.innerWidth + window.innerHeight) / 4;
    };
    window.addEventListener('resize', handleResize);
    handleResize();

    dom.value.appendChild(this.renderer.domElement);
    console.log(this.w, this.h);
    // this.updateArea(w, h)
    layers.sort(function (a, b) {
      return (a.type || 10) - (b.type || 10);
    });
    console.log(this.scene.children, this.scene, 4444444);
    const render = () => {
      requestAnimationFrame(render);
      if (this.repaint !== null) {
        if (this.repaint === 0)
          this.gerber.clearBoard(bottom), this.gerber.clearBoard(top), this.repaint++;
        else {
          // Skip any disabled layers.
          while (this.repaint <= layers.length && !layers[this.repaint - 1].enabled) this.repaint++;

          if (this.repaint <= layers.length) {
            //  this.repaint a layer.
            if (layers[this.repaint - 1].side & this.gerber.BOTTOM)
              this.gerber.renderBoard(bottom, layers[this.repaint - 1], this.limits); //, bottomTexture.needsUpdate = true;
            if (layers[this.repaint - 1].side & this.gerber.TOP)
              this.gerber.renderBoard(top, layers[this.repaint - 1], this.limits); //, topTexture.needsUpdate = true;
          } else if (this.outline && this.outline.enabled) {
            //  this.repaint the outline.
            this.gerber.renderOutline(bottom, this.outline, this.limits); //, bottomTexture.needsUpdate = true;
            this.gerber.renderOutline(top, this.outline, this.limits); //, topTexture.needsUpdate = true;
          }

          // Are we finished  this.repainting?
          if (this.repaint > layers.length)
            (this.repaint = null),
              (bottomTexture.needsUpdate = true),
              (topTexture.needsUpdate = true);
          else this.repaint++;
        }
      }
      this.boardControls.update();
      this.renderer.render(this.scene, this.camera);
    };
    render();
  }
}

export default View3D;
